package com.ynet.middleground.approve.model;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import org.activiti.engine.*;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils;
import org.dozer.Mapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.ynet.core.exception.BusinessException;
import com.ynet.core.log.IfpLogger;
import com.ynet.middleground.approve.bean.*;
import com.ynet.middleground.approve.constant.ProcessStatusEnum;
import com.ynet.middleground.approve.constant.WorkflowConstants;
import com.ynet.middleground.approve.dao.ArProcessExtMapper;
import com.ynet.middleground.approve.dto.ProcessInstanceDTO;
import com.ynet.middleground.approve.dto.TaskDTO;
import com.ynet.middleground.approve.entity.ArProcessExt;
import com.ynet.middleground.approve.utils.BeanUtil;
import com.ynet.middleground.approve.utils.CommUtils;
import com.ynet.middleground.user.dto.ChannelDTO;
import com.ynet.middleground.user.dto.UserBaseInformationDto;
import com.ynet.middleground.user.dto.UserInfoDto;

/**
 * @author liulx
 * @description 流程实例相关业务处理
 * @date 2019-10-20 22:42
 */
@Component
public class ProcessRuntimeModel {

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private ProcessTaskModel processTaskModel;

    @Autowired
    private IdentityService identityService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private Mapper mapper;

    @Autowired
    private UserCenterModel userCenterModel;

    @Autowired
    private ArProcessExtMapper arProcessExtMapper;

    /**
     * 流程优先级；0：一般；1：紧急
     */
    private static final Integer PRIORITY_ZERO = 0;
    private static final Integer PRIORITY_ONE = 1;

    /**
     * 挂起或激活指定流程的实例
     *
     * @param req 激活流程对象: {流程实例id, 激活挂起标志}
     */
    public void suspendOrActiveProcessInstance(ActivateProcessInstanceReq req) {
        String processInstanceId = req.getProcessInstanceId();
        if (StringUtils.isBlank(processInstanceId)) {
            throw new BusinessException("流程实例ID不能为空", "ECAP0001");
        }
        int type = req.getType();
        // 根据流程实例id查询流程实例
        ProcessInstance processInstance =
            runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (processInstance == null) {
            throw new BusinessException("找不到该流程实例信息", "ECAP0200");
        }
        // 是否挂起
        boolean suspend = processInstance.isSuspended();
        if (suspend) {
            if (type == ProcessStatusEnum.ACTIVE.getCode()) {
                // 如果暂挂起则激活
                runtimeService.activateProcessInstanceById(processInstanceId);
            } else {
                throw new BusinessException("该流程实例已经挂起，无需再次挂起", "ECAP0201");
            }
        } else {
            if (type == ProcessStatusEnum.SUSPEND.getCode()) {
                // 如果激活则挂起
                runtimeService.suspendProcessInstanceById(processInstanceId);
            } else {
                throw new BusinessException("该流程实例已经激活，无需再次激活", "ECAP0202");
            }
        }
    }

    /**
     * 根据流程实例ID查询流程是否结束 <br>
     *
     * @param req 流程是否结束请求对象: {流程实例id}
     * @return 流程对象
     */
    public ProcessInstanceDTO isFinishProcess(ProcessInstanceReq req) {
        if (StringUtils.isBlank(req.getProcessInstanceId())) {
            throw new BusinessException("流程实例ID不能为空", "ECAP0001");
        }
        HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery()
            .processInstanceId(req.getProcessInstanceId()).singleResult();
        Optional.ofNullable(processInstance).orElseThrow(() -> new BusinessException("流程实例信息不存在", "ECAP0200"));
        return getProcessInstanceDto(req.getProcessInstanceId());
    }

    /**
     * 根据流程实例ID获取运行流程信息
     * 
     * @param processInstanceId 流程实例ID
     * @return 流程实例对象
     */
    public ProcessInstance getProcessInstance(String processInstanceId) {
        return runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId)
            .includeProcessVariables().singleResult();
    }

    /**
     * 根据流程实例ID获取运行流程信息
     * 
     * @param processInstanceId 流程实例ID
     * @return 流程实例数据传输对象
     */
    public ProcessInstanceDTO getProcessInstanceDto(String processInstanceId) {
        ProcessInstance processInstance = getProcessInstance(processInstanceId);
        return processInstance == null ? null : mapper.map(processInstance, ProcessInstanceDTO.class);
    }

    /**
     * 根据发起人查询用户发起的未结束的流程列表 <br />
     * 解决方案2, 方案1参考 {@link com.ynet.middleground.approve.model.ProcessHistoryModel#listUnfinishedProcessByStartUserId}
     *
     * <p>
     * 是否需要添加渠道判断, 目前考虑不应该加
     * </p>
     *
     * @param req 查询请求对象: {流程发起人}
     * @return 由用户发起的未结束的流程列表
     */
    public List<ProcessInstanceDTO> listProcessInstanceByStartUserId(QueryRunningProcessReq req) {
        String startUserId = req.getStartUserId();
        if (StringUtils.isBlank(startUserId)) {
            throw new BusinessException("流程发起人不能为空", "ECAP0001");
        }
        List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().startedBy(startUserId).list();
        List<ProcessInstanceDTO> resultList = BeanUtil.mapList(mapper, list, ProcessInstanceDTO.class);
        IfpLogger.info("ProcessHistoryModel.listUnfinishedProcessByStartUserId", "用户: {}，未完成流程实例列表: {}", startUserId,
            resultList.toString());
        return resultList;
    }

    /**
     * 判断用户是否参与了未结束的流程(包括用户发起的流程、需要用户正在审核的流程)
     * 
     * @param req 查询请求对象: {流程发起人}
     * @return true 有参与未结束的流程; false 没有参与
     */
    public Boolean isJoinUnfinishedProcess(QueryRunningProcessReq req) {
        String startUserId = req.getStartUserId();
        if (StringUtils.isBlank(startUserId)) {
            throw new BusinessException("流程发起人不能为空", "ECAP0001");
        }
        List<TaskDTO> taskList = processTaskModel.listAuditingTaskByUserId(startUserId);
        List<ProcessInstanceDTO> instanceList = listProcessInstanceByStartUserId(req);
        return (!taskList.isEmpty() || !instanceList.isEmpty());
    }

    /**
     * 启动流程-通过流程定义Key(模板ID) 、业务Key启动流程 <br />
     * 
     * @param startProcessInstanceReq 启动流程请求对象
     * @return 启动的流程实例ID
     */
    @Transactional(rollbackFor = Exception.class)
    public Task startProcessInstance(StartProcessInstanceReq startProcessInstanceReq) {
        String startUserId = startProcessInstanceReq.getStartUserId();
        // 用来设置启动流程的人员ID，引擎会自动把用户ID保存到activiti:initiator中
        // 如果不通过这个方式设置发起人信息，凡是startUserId的API均无法使用
        identityService.setAuthenticatedUserId(startUserId);
        // 流程配置参数
        Map<String, Object> variables = startProcessInstanceReq.getVariables();
        variables = Optional.ofNullable(variables).orElse(Maps.newHashMap());
        // 需要在启动流程参数中添加渠道标示，用于区别用户是在哪个渠道查询待办任务
        variables.put(WorkflowConstants.CHANNEL_SIGNAL, startProcessInstanceReq.getChannel());
        variables.put(WorkflowConstants.START_USER_ID, startUserId);
        // 添加流程的主体业务人
        variables.put(WorkflowConstants.BUSINESS_USER_ID,
            (StringUtils.isEmpty(startProcessInstanceReq.getBusinessUser()) ? startUserId
                : startProcessInstanceReq.getBusinessUser()));
        variables.put(WorkflowConstants.OPERATION_USER_ID, startProcessInstanceReq.getOperationUserId());

        // add by liwq on 2021-03-16 start
        // 移动审批，向流程变量里多添加几个参数，用于查询流程时使用。
        // 校验业务数据
        List<BusinessModuleData> businessModuleData = startProcessInstanceReq.getBusinessData();
        // 若是传空数组过来，就认为是要赋值空数组给变量池
        if (businessModuleData != null && businessModuleData.size() == 0) {
            variables.put(WorkflowConstants.FLOW_BUSINESS_DATA, startProcessInstanceReq.getBusinessData());
        }
        // 若是传null过来，就不做处理。不为null并且数组大于0，校验并排序之后，才放入变量池
        if (businessModuleData != null && businessModuleData.size() > 0) {
            validBusinessData(businessModuleData);
            List<BusinessModuleData> sortList = sortBusinessData(businessModuleData);
            variables.put(WorkflowConstants.FLOW_BUSINESS_DATA, sortList);
        }
        // 公司名称
        if (!StringUtils.isEmpty(startProcessInstanceReq.getCompanyName())) {
            variables.put(WorkflowConstants.FLOW_COMPANY_NAME, startProcessInstanceReq.getCompanyName());
        }
        // 产品名称
        if (!StringUtils.isEmpty(startProcessInstanceReq.getProductName())) {
            variables.put(WorkflowConstants.FLOW_PRODUCT_NAME, startProcessInstanceReq.getProductName());
        }
        // 流程摘要
        if (!StringUtils.isEmpty(startProcessInstanceReq.getProcessRemark())) {
            variables.put(WorkflowConstants.FLOW_BUSINESS_SUMMARY, startProcessInstanceReq.getProcessRemark());
        }
        // 发起人姓名
        String userName = null;
        List<UserInfoDto> userInfoDtos = userCenterModel.queryUserInfo(startProcessInstanceReq.getStartUserId(),
            startProcessInstanceReq.getChannel(), startProcessInstanceReq.getStartUserId());
        if (userInfoDtos != null && userInfoDtos.size() > 0) {
            userName = userInfoDtos.get(0).getUserBaseInformationDto().getUserName();
        }
        if (!StringUtils.isEmpty(userName)) {
            variables.put(WorkflowConstants.FLOW_START_USER_NAME, userName);
        }
        // 金控经办人员ID
        if (startProcessInstanceReq.getQdpfOperatorId() != null) {
            variables.put(WorkflowConstants.FLOW_BUSINESS_QDPF_OPERATOR, startProcessInstanceReq.getQdpfOperatorId());
            // 金控经办人员姓名
            String qdpfOperatorName = null;
            UserBaseInformationDto base = null;
            List<UserInfoDto> userInfo =
                userCenterModel.queryUserInfo(String.valueOf(startProcessInstanceReq.getQdpfOperatorId()),
                    startProcessInstanceReq.getChannel(), startProcessInstanceReq.getStartUserId());
            if (userInfo != null && userInfo.size() > 0) {
                base = userInfo.get(0).getUserBaseInformationDto();
                if (base == null || !"1".equals(base.getUserType())) {
                    throw new BusinessException("金控经办人员不存在", "ECAP0509");
                }
                qdpfOperatorName = base.getUserName();
                variables.put(WorkflowConstants.FLOW_BUSINESS_QDPF_OPERATOR_NAME, qdpfOperatorName);
                // 金控经办人员钉钉ID
                if (StringUtils.isNotEmpty(base.getDingtalkUserid())) {
                    variables.put(WorkflowConstants.FLOW_BUSINESS_QDPF_DINGTALK_USERID, base.getDingtalkUserid());
                }
            } else {
                throw new BusinessException("金控经办人员不存在", "ECAP0509");
            }

        }
        // 流程优先级
        if (!PRIORITY_ZERO.equals(startProcessInstanceReq.getProcessPriority())
            && !PRIORITY_ONE.equals(startProcessInstanceReq.getProcessPriority())) {
            throw new BusinessException("请上传合法优先级", "ECAP0207");
        }
        variables.put(WorkflowConstants.FLOW_PROCESS_PRIORITY, startProcessInstanceReq.getProcessPriority());

        // 业务场景
        if (!StringUtils.isEmpty(startProcessInstanceReq.getBusinessScene())) {
            variables.put(WorkflowConstants.FLOW_BUSINESS_SCENE, startProcessInstanceReq.getBusinessScene());
        }
        // add by liwq on 2021-03-16 end

        // add By liwq on 2021-1-25 Start
        // 跨渠道审批相关
        // 可以处理该流程实例的渠道标志，默认：当前渠道
        if (StringUtils.isEmpty(startProcessInstanceReq.getFlowCanOperateChannelSignal())) {
            variables.put(WorkflowConstants.FLOW_CAN_OPERATE_CHANNEL_SIGNAL, startProcessInstanceReq.getChannel());
        }
        if (!StringUtils.isEmpty(startProcessInstanceReq.getFlowCanOperateChannelSignal())) {
            Boolean channelFlag = CommUtils.checkChannel(startProcessInstanceReq.getFlowCanOperateChannelSignal());
            if (!channelFlag) {
                throw new BusinessException("可以处理该流程实例的渠道标志包含不合规则字符，请检查", "ECAP0205");
            }
            variables.put(WorkflowConstants.FLOW_CAN_OPERATE_CHANNEL_SIGNAL,
                startProcessInstanceReq.getFlowCanOperateChannelSignal());
        }
        // add By liwq on 2021-1-25 End

        // add By liulx on 2023-03-09 | Start
        if (StringUtils.isNotEmpty(startProcessInstanceReq.getBusinessStartTime())) {
            variables.put(WorkflowConstants.FLOW_START_TIME, startProcessInstanceReq.getBusinessStartTime());
        }

        if (StringUtils.isNotEmpty(startProcessInstanceReq.getStartFormatId())) {
            variables.put(WorkflowConstants.FORMAT_SIGNAL, startProcessInstanceReq.getStartFormatId());
        }

        if (!CollectionUtils.isEmpty(startProcessInstanceReq.getToBeSignedList())) {
            variables.put(WorkflowConstants.FLOW_LIST_OF_FILES_TO_BE_SIGNED,
                startProcessInstanceReq.getToBeSignedList());
        }
        // add By liulx on 2023-03-09 | End

        String processDefinitionKey = startProcessInstanceReq.getProcessDefinitionKey();
        ProcessInstance processInstance;
        try {
            String businessKey = startProcessInstanceReq.getBusinessKey();
            // 如果业务key不为空，以关联业务key的方式启动流程
            if (StringUtils.isNotBlank(businessKey)) {
                variables.put(WorkflowConstants.BUSINESS_KEY, businessKey);
                // 为方便区分，这里业务Key格式为: 渠道.流程定义Key.业务Key eg: xiaodai.leave.1
                String businessKeyFormat =
                    startProcessInstanceReq.getChannel() + "." + processDefinitionKey + "." + businessKey;
                processInstance =
                    runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKeyFormat, variables);
            } else {
                // 业务key 为空，直接以流程key启动
                processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, variables);
            }
        } catch (ActivitiObjectNotFoundException e) {
            throw new BusinessException("找不到 [" + processDefinitionKey + "] 的流程", "ECAP0100");
        } catch (ActivitiException e) {
            throw new BusinessException("流程配置异常:" + e.getMessage(), "ECAP0004");
        }

        IfpLogger.info(Thread.currentThread().getStackTrace()[1].getMethodName(), "用户: {}, 流程实例: {}", startUserId,
            processInstance.toString());

        // add By liwq on 2021-03-17 start
        // 将流程名称放到变量里
        runtimeService.setVariable(processInstance.getProcessInstanceId(), WorkflowConstants.FLOW_PROCESS_NAME,
            processInstance.getProcessDefinitionName());
        // 将流程实例ID放到变量里
        runtimeService.setVariable(processInstance.getProcessInstanceId(), WorkflowConstants.FLOW_PROCESS_INSTANCE_ID,
            processInstance.getProcessInstanceId());
        // 将流程发起时间放到变量里面，优先使用渠道上送的 业务实际发起时间
        runtimeService.setVariable(processInstance.getProcessInstanceId(), WorkflowConstants.FLOW_START_TIME,
            StringUtils.isEmpty(startProcessInstanceReq.getBusinessStartTime())
                ? processInstance.getStartTime().getTime() : startProcessInstanceReq.getBusinessStartTime());

        // 查询第一个任务，并执行完成
        Task task = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).active()
            .singleResult();
        processTaskModel.completeFirstTask(startProcessInstanceReq, task);

        // 往流程优先级表里，添加一条记录
        ArProcessExt arProcessExt = new ArProcessExt();
        arProcessExt.setProcessInstanceId(processInstance.getProcessInstanceId());
        arProcessExt.setPriority(startProcessInstanceReq.getProcessPriority());
        arProcessExt.setGmtCreate(LocalDateTime.now());
        arProcessExt.setCreateBy(startProcessInstanceReq.getOperationUserId());
        arProcessExt.setGmtModified(LocalDateTime.now());
        arProcessExt.setModifiedBy(startProcessInstanceReq.getOperationUserId());
        arProcessExt.setBusinessStartTime(StringUtils.isEmpty(startProcessInstanceReq.getBusinessStartTime())
            ? String.valueOf(processInstance.getStartTime().getTime())
            : startProcessInstanceReq.getBusinessStartTime());
        arProcessExtMapper.insert(arProcessExt);
        // add By liwq on 2021-03-17 end
        return task;
    }

    /**
     * 对业务数据排序
     * 
     * @param businessModuleData
     */
    public List<BusinessModuleData> sortBusinessData(List<BusinessModuleData> businessModuleData) {
        // Tab组排序
        businessModuleData = businessModuleData.stream()
            .sorted(Comparator.comparing(BusinessModuleData::getOrder).reversed()).collect(Collectors.toList());
        // 每一个Tab里的分组排序
        businessModuleData.forEach(b -> b.setData(b.getData().stream()
            .sorted(Comparator.comparing(BusinessGroupData::getOrder).reversed()).collect(Collectors.toList())));
        // 每一个分组里的标签排序
        businessModuleData.forEach(b -> b.getData().forEach(c -> c.setData(c.getData().stream()
            .sorted(Comparator.comparing(BusinessTagData::getOrder).reversed()).collect(Collectors.toList()))));
        // 每一个标签里的表单排序
        businessModuleData.forEach(b -> b.getData().forEach(c -> c.getData().forEach(d -> d.setData(d.getData().stream()
            .sorted(Comparator.comparing(BusinessFormData::getOrder).reversed()).collect(Collectors.toList())))));
        return businessModuleData;
    }

    /**
     * 校验业务数据
     * 
     * @author liwq
     * @param businessModuleData
     */
    public void validBusinessData(List<BusinessModuleData> businessModuleData) {
        // 层级一：循环校验每一个Tab页
        businessModuleData.forEach(b -> {
            // 层级二: 每一个Tab都有分组模块，循环校验分组,
            List<BusinessGroupData> groupData = b.getData();
            groupData.forEach(g -> {
                // 层级三：每一个分组模块都有许多标签数据
                List<BusinessTagData> businessTagData = g.getData();
                businessTagData.forEach(h -> {
                    // 层级三：每一个分组模块都有许多业务表单数据，循环校验表单数据（主要校验的就是表单数据）
                    List<BusinessFormData> businessFormData = h.getData();
                    businessFormData.forEach(f -> {
                        // 校验点①：参数类型
                        String type = f.getType();
                        if (!"text".equals(type) && !"textarea".equals(type) && !"img".equals(type)
                            && !"file".equals(type) && !"table".equals(type)) {
                            throw new BusinessException("请上传合法参数类型", "ECAP1001");
                        }
                        // 校验点②：若参数类型是table表格，则columns标题字段不能为空;
                        // 并且校验value传参key 是否在Column列表之内
                        if ("table".equals(type)) {
                            List<BusinessTableData> columns = f.getColumns();
                            if (columns == null || columns.size() == 0) {
                                throw new BusinessException("请上传表格列信息集合", "ECAP1002");
                            }
                            // 表格的所有行标题List
                            List<String> titleList = new ArrayList<>(10);
                            for (BusinessTableData c : columns) {
                                String title = c.getName();
                                titleList.add(title);
                            }
                            // 表格的所有行数据key List
                            String value = f.getValue();
                            if (StringUtils.isEmpty(value)) {
                                throw new BusinessException("请上传参数值", "ECAP1005");
                            }
                            JSONArray object = JSONArray.parseArray(value);
                            for (Object o : object) {
                                JSONObject jsonObject1 = (JSONObject)o;
                                // 一行一行检查,看看是否有多余的值
                                List valueKeyList = Arrays.asList(jsonObject1.keySet().toArray());
                                if (!titleList.containsAll(valueKeyList)) {
                                    throw new BusinessException("请上传合法参数值：" + valueKeyList.toString(), "ECAP1004");
                                }
                                // 一行一行检查,看看是否有少的值
                                List<String> tempTitleList = new ArrayList<String>(10);
                                tempTitleList.addAll(titleList);
                                tempTitleList.removeAll(valueKeyList);
                                if (tempTitleList.size() > 0) {
                                    throw new BusinessException("参数值缺少：" + tempTitleList.toString(), "ECAP1006");
                                }
                                tempTitleList.clear();
                            }
                        }
                        // 校验点③：若参数类型是file合约文件，且是否签章字段为true,则signInfo签章位置信息不能为空
                        // if ("file".equals(type) && f.getIsSign()) {
                        // BusinessSignInfoData signInfo = f.getSignInfo();
                        // if (signInfo == null) {
                        // throw new BusinessException("请上传签章位置信息", "ECAP1003");
                        // }
                        // }
                    });
                });
            });

        });

    }

    /**
     * 作废流程
     * 
     * @param cancelProcessReq 作废流程请求对象
     */
    public void cancelProcess(CancelProcessReq cancelProcessReq) {
        ProcessInstance processInstance = getProcessInstance(cancelProcessReq.getProcessInstanceId());
        Optional.ofNullable(processInstance).orElseThrow(() -> new BusinessException("流程实例信息不存在", "ECAP0200"));
        runtimeService.deleteProcessInstance(cancelProcessReq.getProcessInstanceId(), cancelProcessReq.getReason());
    }

    /**
     * 根据流程主体人查询是否有在途流程
     * 
     * @param req 查询业务客户是否有未结束的流程请求参数
     */
    public void listProcessInstanceByBusinessUserId(QueryBusinessUserRunningProcessReq req) {
        String businessUser = req.getBusinessUser();
        if (StringUtils.isBlank(businessUser)) {
            throw new BusinessException("流程业务主体人不能为空", "ECAP0001");
        }
        List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().includeProcessVariables()
            .variableValueEquals(WorkflowConstants.BUSINESS_USER_ID, businessUser).list();
        // 存在在途流程提示信息
        StringBuilder builder = new StringBuilder();
        if (list != null && !list.isEmpty()) {
            Map<String, String> channelMap = new HashMap<>(16);
            list.forEach(p -> {
                Object channelObj = p.getProcessVariables().get(WorkflowConstants.CHANNEL_SIGNAL);
                if (channelObj != null) {
                    ChannelDTO channelDTO = userCenterModel.queryChannelByCode(req.getOperationUserId(),
                        String.valueOf(channelObj), req.getChannel());
                    channelMap.put(channelDTO.getChannelCode(), channelDTO.getChannelName());
                }
            });
            channelMap.forEach((code, value) -> {
                builder.append(value);
                builder.append("，");
            });
            builder.append("存在内部在途流程");
            throw new BusinessException(builder.toString(), "ECAP0205");
        }

        // TODO: 待对接渠道，企业自己的流程由渠道自己管理，需要查询各个渠道
        // throw new BusinessException("xxxx（渠道名称）存在企业在途流程", "ECAP0205");
        // xxxx（渠道名称） 存在内部在途流程、xxxx（渠道名称）存在企业在途流程
    }

    /**
     * 更改流程优先级
     * 
     * @param req 更改流程优先级请求参数
     */
    public void updateProcessPriority(PriorityReq req) {
        ProcessInstance processInstance = getProcessInstance(req.getProcessInstanceId());
        Optional.ofNullable(processInstance).orElseThrow(() -> new BusinessException("流程实例信息不存在", "ECAP0200"));
        runtimeService.setVariable(req.getProcessInstanceId(), WorkflowConstants.FLOW_PROCESS_PRIORITY,
            req.getProcessPriority());
    }

}
